#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include "dominion.h"
#include "dominion_helpers.h"
#include "interface.h"

#define MAX_ITER 1000

void fprintState(struct gameState *game, FILE *fp) {
  int numActions = game->numActions;
  int numCoins = game->coins;
  int numBuys = game->numBuys;
  int currentPlayer = game->whoseTurn;
  int phase = game->phase;
  char phaseName[MAX_STRING_LENGTH];
  phaseNumToName(phase,phaseName);
  fprintf(fp, "Player %d:\n%s phase\n%d actions\n%d coins\n%d buys\n\n", currentPlayer, phaseName, numActions, numCoins, numBuys);
}

void fprintScores(struct gameState *game, FILE *fp) {
  int playerNum, score[MAX_PLAYERS];
  int numPlayers = game->numPlayers;
  for(playerNum = 0; playerNum < numPlayers; playerNum++) {
    score[playerNum] = scoreFor(playerNum,game);
    fprintf(fp, "Player %d has a score of %d\n", playerNum, score[playerNum]);
  }
}

void fprintHand(int player, struct gameState *game, FILE *fp) {
  int handCount = game->handCount[player];
  int handIndex;
  fprintf(fp, "Player %d's hand:\n", player);
  if(handCount > 0) fprintf(fp, "#  Card\n");
  for(handIndex = 0; handIndex < handCount; handIndex++) {
    int card = game->hand[player][handIndex];
    char name[MAX_STRING_LENGTH];
    cardNumToName(card, name);
    fprintf(fp, "%-2d %-13s\n", handIndex, name);
  }
  fprintf(fp, "\n");
}


int randomRange(int min, int max) {
    return rand() % (max - min + 1) + min;
}

int handHasEstate(struct gameState *state) {
    int player = state->whoseTurn;
    int handsize = numHandCards(state);
    int *hand = state->hand[player];
    for (int i = 0; i < handsize; ++i) {
        if (hand[i] == estate)
            return 1;
    }
    return 0;
}

int handHasMoney(struct gameState *state) {
    int player = state->whoseTurn;
    int handsize = numHandCards(state);
    int *hand = state->hand[player];
    for (int i = 0; i < handsize; ++i) {
        if (hand[i] == copper || hand[i] == silver || hand[i] == gold)
            return 1;
    }
    return 0;
}

void printCard(int card, FILE *fp) {
    char buff[50];
    cardNumToName(card, &buff);
    fprintf(fp, "%s\n", buff);
}

void printCards(int *cards, int num, FILE *fp) {
    char buff[50];
    for (int i = 0; i < num; ++i) {
        cardNumToName(cards[i], &buff);
        fprintf(fp, "%s, ", buff);
    }
}

void randomKingdomCards(int* cards) {
    int kingdom_cards[10];
    int dup;
    int card;
    int num_picked = 0;
    while (num_picked < 10) {
        do {
            dup = 0;
            card = randomRange(adventurer, treasure_map);
            // prevent duplicates
            for (int i = 0; i < num_picked; ++i) {
                if (kingdom_cards[i] == card) {
                    dup = 1;
                    break;
                }
            }
        } while (dup);
        kingdom_cards[num_picked] = card;
        num_picked++;
    }
    memcpy(cards, kingdom_cards, sizeof(int) * 10);
}

/*
Pick action cards first, or treasure map
*/
int pickFavoredCard(int player, struct gameState *state) {
    int handsize = numHandCards(state);
    int *hand = state->hand[player];
    int curr;
    for (int i = 0; i < handsize; ++i) {
        curr = hand[i];
        // Play action cards first
        if (curr == village ||  curr == great_hall || curr == minion)
            return i;
    }
    for (int i = 0; i < handsize; ++i) {
        curr = hand[i];
        if (curr > gold && curr != gardens) {
            // If its a treasure map, check our hand for another treasure map
            if (curr == treasure_map) {
                // dont look at cards we've passed already
                for (int j = i+1; j < handsize; ++j) {
                    if (hand[j] == treasure_map)
                        return i;
                }
            }
        }
    }
    return -1;
}

/*
Pick action cards or treasure map first. Otherwise pick something at random.
*/
int pickCard(int player, struct gameState *state) {
    int card = pickFavoredCard(player, state);
    if (card != -1)
        return card;
    int handsize = numHandCards(state);
    int *hand = state->hand[player];
    // Check we have a card in our hand we can play
    int playable_card = 0;
    for (int i = 0; i < handsize; ++i) {
        card = hand[i];
        /* Explicit less than. We dont count a treasure_map as playable, because
        it would have been chosen in the pickFavoredCard function if we could
        play it */
        if (card >= adventurer && card < treasure_map && card != gardens) {
            playable_card = 1;
            // fprintf(fp, "Found playable card. i: %d, card: ", i);
            // printCard(hand[i]);
            break;
        }
    }
    // No playable cards found
    if (playable_card == 0)
        return -1;
    // pick a random playable card
    int cardIndex;
    cardIndex = randomRange(0, handsize-1);
    while (hand[cardIndex] < adventurer ||
            // Treasure map is an invalid card at this point as well
            hand[cardIndex] >= treasure_map ||
            hand[cardIndex] == gardens) {
        cardIndex = randomRange(0, handsize);
    }
    // fprintf(fp, "cardIndex: %d\ncard: ", cardIndex);
    // printCard(hand[cardIndex]);
    return cardIndex;
}

int actionPhase(struct gameState *state, FILE *fp) {
    int cardIndex, card;
    int choice1, choice2, choice3;
    int cardRet;
    int currentPlayer = state->whoseTurn;
    int handsize = numHandCards(state);

    cardIndex = pickCard(currentPlayer, state);
    if (cardIndex == -1) {
        fprintf(fp, "No playable cards\n\n");
        return -1;
    }
    card = handCard(cardIndex, state);
    // Feast is broken as crap, I'm making it a no-op
    if (card == feast) {
        fprintf(fp, "Playing feast with no effect.\n");
        state->numActions--;
        discardCard(cardIndex, currentPlayer, state, 0);
        return 0;
    }
    // If the card is a mine and we have no money, then try a new card from our hand
    if ((card == mine) && !handHasMoney(state)) {
        fprintf(fp, "Skipping mine, we have no money\n");
        return 1;
    }
    // Play the card
    int count = 0;
    do {
        // Prevent infinite looping
        if (count > MAX_ITER) {
            fprintf(fp, "After %d iterations, could not play ", MAX_ITER);
            printCard(card, fp);
            discardCard(cardIndex, currentPlayer, state, 0);
            return -1;
        }
        // Card's with first choice being a hand#
        if (card == mine || card == remodel ||
            card == ambassador || card == salvager) {
            choice1= randomRange(0, handsize);
            if (card == ambassador)
                choice2= randomRange(0, handsize);
            else
                choice2 = randomRange(0, 26);
        } else if (card == steward) {
            choice1 = randomRange(1, 3);
            choice2 = randomRange(0, handsize);
        } else if (card == baron) {
            if (handHasEstate(state))
                choice1 = randomRange(0, 1);
            else
                choice1 = 0;
        } else {
            choice1 = randomRange(0, 10);
            choice2 = randomRange(0, 26);
        }
        choice3 = randomRange(0, handsize);

        // fprintf(fp, "Count: %d\n", count);
        // fprintf(fp, "index: %d\n", cardIndex);
        // fprintf(fp, "choice1 %d\n", choice1);
        // fprintf(fp, "choice2 %d\n", choice2);
        // fprintf(fp, "choice3 %d\n", choice3);

        cardRet = playCard(cardIndex, choice1, choice2, choice3, state);
        if (cardRet == -1) {
            // fprintf(fp, "failed to play card ");
            // printCard(state->hand[currentPlayer][cardIndex]);
        }
        count++;
    } while(cardRet == -1);
    // Special case, doesn't discard itself
    if (card == tribute) {
        discardCard(cardIndex, currentPlayer, state, 0);
    }
    fprintf(fp, "Played card: ");
    printCard(card, fp);
    fprintf(fp, "\n");
    // printPlayed(currentPlayer, state);
    return 0;
}

int buyPhase(struct gameState *state, FILE *fp) {
    int coins = state->coins;
    // Always try to buy a province
    if (coins >= PROVINCE_COST)
        return buyCard(province, state);
    int card;
    int boughtCard = -1;
    int counter = 0;
    do {
        if (counter > MAX_ITER) {
            fprintf(fp, "Couldn't buy anything after %d iterations.\n", MAX_ITER);
            return -1;
        }
        // Try buying a random card until we succeed
        card = randomRange(estate, treasure_map);
        boughtCard = buyCard(card, state);
        counter++;
    } while (boughtCard != 0);
    // Successfully bought a card
    fprintf(fp, "Successfully bought card: ");
    printCard(card, fp);

    return 0;
}

void playRandomGame(int seed, FILE *fp) {
    srand(seed);
    fprintf(fp, "Seed: %d\n", seed);

    struct gameState state;
    int kingdom_cards[10];

    randomKingdomCards(kingdom_cards);
    fprintf(fp, "Kingdom Cards:\n");
    printCards(kingdom_cards, 10, fp);

    int numPlayers = randomRange(2, 4);
    initializeGame(numPlayers, kingdom_cards, seed, &state);
    fprintf(fp, "\nStarting Game\n");
    fprintf(fp, "Number of players: %d\n\n", numPlayers);

    /*
    int fakeHand[] = {adventurer, council_room, feast, gardens, mine, remodel,
       smithy, village, baron, great_hall, minion, steward, tribute, ambassador,
       cutpurse, embargo, outpost, salvager, sea_hag, treasure_map};
    int fakeHand[] = {adventurer, council_room, feast, gardens, mine, copper};
    memcpy(state.hand[0], fakeHand, sizeof(fakeHand));
   */

    int iter = 0;
    while (!isGameOver(&state)) {
        // if (iter > 10) return;
        iter++;
        int currentPlayer = state.whoseTurn;
        fprintf(fp, "Player %d's turn!\n", currentPlayer);

        updateCoins(currentPlayer, &state, 0);
        fprintState(&state, fp);
        fprintHand(currentPlayer, &state, fp);

        int action;
        int counter = 0;
        while (state.numActions > 0) {
            if (counter > 5) {
                fprintf(fp, "No playable cards in hand.\n");
                break;
            }
            action = actionPhase(&state, fp);
            if (action == -1)
                break;
            if (action == 1) {
                counter++;
                continue;
            }
        }
        int buy;
        while (state.numBuys > 0) {
            buy = buyPhase(&state, fp);
            // Didn't buy anything.
            if (buy == -1) {
                break;
            }
        }

        fprintState(&state, fp);
        fprintHand(currentPlayer, &state, fp);
        fprintf(fp, "End of player %d's turn\n\n", currentPlayer);
        endTurn(&state);
    }
    fprintf(fp, "Game is over!\n");

    int winners[MAX_PLAYERS];
    getWinners(winners, &state);

    fprintScores(&state, fp);

    for (int i = 0; i < MAX_PLAYERS; ++i) {
        if (winners[i] == 1) {
            fprintf(fp, "\nPlayer %d is the winner!\n", i);
        }
    }


}

int main(int argc, char **argv) {
    int seed = 1;
    int iterations = 1;
    if (argc > 1)
        seed = atoi(argv[1]);
    if (argc > 2)
        iterations = atoi(argv[2]);

    char *filename = "gameResults.out";
    FILE *fp = fopen(filename, "w+");

    for (int i = 0; i < iterations; ++i) {
        playRandomGame(seed, fp);
        seed++;
    }

    fclose(fp);

    return 0;
}
